using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Resources.Tools;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common;
using Microsoft.Practices.ObjectBuilder2;

namespace Common.Unity.Extensions.Serialization
{
	public class TypeResourceStreamResolver : IStreamResolver
	{
		readonly string resourceName;
		readonly Type resourceType;
		readonly static Dictionary<Assembly,Type> cache = new Dictionary<Assembly, Type>();

		public TypeResourceStreamResolver( Type resourceType, string resourceName )
		{
			Microsoft.Practices.Unity.Utility.Guard.ArgumentNotNullOrEmpty( resourceName, "resourceName" );
			this.resourceName = resourceName;
			this.resourceType = resourceType ?? ResolveDefaultResourceType();
		}

		static Type ResolveDefaultResourceType()
		{
			Assembly key = Assembly.GetCallingAssembly();
			if ( !cache.ContainsKey( key ) )
			{
				cache[key] = ResolveType( key );
			}
			return cache[key];
		}

		static Type ResolveType( Assembly key )
		{
			foreach ( var type in key.GetTypes() )
			{
				var attribute = Attribute.GetCustomAttribute( type, typeof(GeneratedCodeAttribute) ) as GeneratedCodeAttribute;
				if ( attribute != null && attribute.Tool == typeof(StronglyTypedResourceBuilder).FullName )
				{
					return type;
				}
			}
			throw new InvalidOperationException( string.Format( "No default resource type found for assembly {0}", key.FullName ) );
		}

		public Type ResourceType
		{
			get { return resourceType; }
		}

		public string ResourceName
		{
			get { return resourceName; }
		}

		public Stream ResolveStream( IBuilderContext context )
		{
			var source = ResourceStringLoader.LoadString( ResourceType.FullName, ResourceName, ResourceType.Assembly );
			if ( !string.IsNullOrEmpty( source ) )
			{
				return new MemoryStream( Encoding.Default.GetBytes( source ) );
			}
			throw new InvalidOperationException( string.Format( "Could not find resource '{0}' in type '{1}'.", ResourceName, ResourceType ) );
		}
	}
}